package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.ArticleFreemarkerService;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.redis.CacheService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.mess.ArticleVisitStreamMess;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 文章信息表，存储已发布的文章 服务实现类
 * </p>
 *
 * @author itheima
 */
@Slf4j
@Service
@Transactional
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

    // 单页最大加载的数字
    private final static short MAX_PAGE_SIZE = 50;

    @Autowired
    private ApArticleMapper apArticleMapper;

    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;

    @Autowired
    private ApArticleContentMapper apArticleContentMapper;

    @Autowired
    private ArticleFreemarkerService freemarkerService;

    @Autowired
    private CacheService cacheService;

    /**
     * 根据参数加载文章列表
     *
     * @param loadType 1为加载更多  2为加载最新
     * @param dto
     * @return
     */
    @Override
    public ResponseResult load(ArticleHomeDto dto, Byte loadType) {
        //1.参数校验
        Integer size = dto.getSize();
        if (size == null || size == 0) {
            size = 10;
        }
        size = Math.min(size, MAX_PAGE_SIZE);
        //文章频道校验
        if (StringUtils.isEmpty(dto.getTag())) {
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        //时间校验
        if (dto.getMaxBehotTime() == null) dto.setMaxBehotTime(new Date());
        if (dto.getMinBehotTime() == null) dto.setMinBehotTime(new Date());

        List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, loadType);
        return ResponseResult.okResult(apArticles);
    }

    /**
     * 加载文章列表
     *
     * @param dto
     * @param type      1 加载更多   2 加载最新
     * @param firstPage true 是首页  false 不是首页
     * @return
     */
    @Override
    public ResponseResult loadArticleListV2(ArticleHomeDto dto, Short type, boolean firstPage) {
        if (firstPage) {
            //查询缓存中的数据
            String jsonStr = cacheService.get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if (StringUtils.isNotBlank(jsonStr)) {
                List<HotArticleVo> hotArticleVos = JSONArray.parseArray(jsonStr, HotArticleVo.class);
                return ResponseResult.okResult(hotArticleVos);
            }
        }
        return this.load(dto, (byte) (short) type);
    }

    /**
     * 远程调用保存文章
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult saveArticle(ArticleDto dto) {
        //1.检查参数
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(dto, apArticle);
        //2.判断id是否存在
        if (dto.getId() == null) {
            //id不存在则为新增
            //保存文章表
            save(apArticle);
            //保存配置表
            ApArticleConfig apArticleConfig = new ApArticleConfig(apArticle.getId());
            apArticleConfigMapper.insert(apArticleConfig);
            //保存内容表
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContent.setContent(dto.getContent());
            apArticleContentMapper.insert(apArticleContent);
            //生成静态文件
            freemarkerService.buildArticleToMinIO(dto.getId());
        } else {
            //id存在则为更新
            freemarkerService.buildArticleToMinIO(dto.getId());
            updateById(apArticle);
            //更新内容表
            ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery()
                    .eq(ApArticleContent::getArticleId, apArticle.getId()));
            apArticleContent.setContent(dto.getContent());
            apArticleContentMapper.updateById(apArticleContent);
        }


       /* try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/

        //返回文章id
        return ResponseResult.okResult(apArticle.getId());
    }


    /**
     * 文章上下架
     *
     * @param map
     * @return
     */
    @Override
    public ResponseResult downOrUp(Map map) {
        if (map.get("id") == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        ApArticle article = getById((long) map.get("id"));
        ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, map.get("id")));
        if (article == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        short enable = (short) map.get("enable");
        if (enable == 1) {
            freemarkerService.buildArticleToMinIO((long) map.get("id"));
        }
        if (enable == 0) {
            article.setStaticUrl(null);
            updateById(article);
        }


        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 更新文章的分值  同时更新缓存中的热点文章数据
     *
     * @param mess
     */
    @Override
    public void updateScore(ArticleVisitStreamMess mess) {
        //1.更新文章的点赞,阅读,收藏,观看数据
        ApArticle article = updateArticle(mess);

        //2.计算文章分值
        Integer score = computeScore(article);
        score = score * 3;

        //3.替换当前文章对应频道的热点数据
        replaceDataToRedis(article, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + article.getChannelId());

        //4.替换推荐文章的热点数据
        replaceDataToRedis(article, score, ArticleConstants.DEFAULT_TAG);
    }


    /**
     * 替换当前文章对应频道的热点数据
     *
     * @param article
     * @param score
     * @param key
     */
    private void replaceDataToRedis(ApArticle article, Integer score, String key) {
        //1.查询出缓存中的所有数据
        String json = cacheService.get(key);
        List<HotArticleVo> vos = JSONArray.parseArray(json, HotArticleVo.class);


        if (vos != null && vos.size() > 0) {

            boolean flag = true;

            for (HotArticleVo vo : vos) {
                //2.如果当前文章在缓存中存在,更新分数即可
                if (vo.getId().equals(article.getId())) {
                    vo.setScore(score);
                    flag = false;
                    break;
                }
            }

            //3.如果当前文章在缓存中不存在,判断缓存中数据是否达到30条
            if (flag) {
                HotArticleVo hotArticleVo = new HotArticleVo();
                BeanUtils.copyProperties(article, hotArticleVo);
                hotArticleVo.setScore(score);

                if (vos.size() >= 30) {

                    //5.缓存中数据大于等于30条,比较当前文章和缓存中分数最低的数据,大于则替换
                    vos = vos.stream()
                            .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                            .collect(Collectors.toList());
                    HotArticleVo articleVo = vos.get(vos.size() - 1);
                    if (articleVo.getScore() < hotArticleVo.getScore()) {
                        vos.remove(articleVo);
                        vos.add(hotArticleVo);
                    }
                } else {
                    vos.add(hotArticleVo);
                }
            }

        }
        //4.缓存中数据不足30条,直接保存当前文章
        vos = vos.stream()
                .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                .collect(Collectors.toList());

        //6.缓存所有数据到redis
        cacheService.set(key, JSON.toJSONString(vos));
    }


    /**
     * 更新文章的点赞,阅读,收藏,观看数据
     *
     * @param mess
     * @return
     */
    private ApArticle updateArticle(ArticleVisitStreamMess mess) {
        //获取当前文章
        ApArticle article = getById(mess.getArticleId());
        if (article != null) {
            //设置参数
            article.setCollection((article.getCollection() == null ? 0 : article.getCollection()) + mess.getCollect());
            article.setComment((article.getComment() == null ? 0 : article.getComment()) + mess.getComment());
            article.setLikes((article.getLikes() == null ? 0 : article.getLikes()) + mess.getLike());
            article.setViews((article.getViews() == null ? 0 : article.getViews()) + mess.getView());
            //更新文章
            updateById(article);
        }

        //返回结果
        return article;
    }

    /**
     * 计算文章具体分值
     *
     * @param article
     * @return
     */
    private Integer computeScore(ApArticle article) {
        Integer score = 0;
        if (article.getLikes() != null) {
            score = +article.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if (article.getCollection() != null) {
            score += article.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        if (article.getComment() != null) {
            score += article.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if (article.getViews() != null) {
            score += article.getViews();
        }
        return score;
    }

}
